package nickthissen.iracing.networking;

import android.content.Context;
import android.util.Log;

import java.util.concurrent.TimeoutException;

import nickthissen.iracing.activities.RecentPopularActivity;
import nickthissen.iracing.common.Urls;
import nickthissen.iracing.common.Error;
import nickthissen.iracing.datamodels.FavThreadsResult;
import nickthissen.iracing.datamodels.FavoriteThread;
import nickthissen.iracing.datamodels.Forum;
import nickthissen.iracing.datamodels.Result;
import nickthissen.iracing.datamodels.Thread;
import nickthissen.iracing.datamodels.StringArrayResult;
import nickthissen.iracing.datamodels.StringResult;
import nickthissen.iracing.datamodels.User;
import nickthissen.iracing.parsing.HttpUtil;

public class Networking 
{
    ///////////////////////////////////////////////////////////////////////
    /// Communication with iRacing membersites
    ///////////////////////////////////////////////////////////////////////

	public static final String LOGIN_URL = "https://members.iracing.com/jforum/Login";
    public static final String NEWTHREAD_URL = "http://members.iracing.com/jforum/jforum.page";
    public static final String POST_URL = "http://members.iracing.com/jforum/jforum.page";
    public static final String EDIT_URL = "http://members.iracing.com/jforum/jforum.page";
    public static final String PM_URL = "http://members.iracing.com/jforum/jforum.page";
    public static final String DRIVERSEARCH_URL = "http://members.iracing.com/membersite/member/GetDriverStatus";

    public static final String FORUMLIST_URL = "http://members.iracing.com/jforum/forums/list.page";
    public static final String PMLIST_URL = "http://members.iracing.com/jforum/pm/inbox.page";
    public static final String SENTPMLIST_URL = "http://members.iracing.com/jforum/pm/sentbox.page";
    public static final String HOME_URL = "http://members.iracing.com/membersite/member/Home.do";
    public static final String RECENT_URL = "http://members.iracing.com/jforum/recentTopics/list.page";
    public static final String POPULAR_URL = "http://members.iracing.com/jforum/hottestTopics/list.page";

    public static final String WATCH_URL = "http://members.iracing.com/jforum/posts/watch/0/%s.page";
    public static final String UNWATCH_URL = "http://members.iracing.com/jforum/posts/unwatch/0/%s.page";
    public static final String WATCH_FORUM_URL = "http://members.iracing.com/jforum/forums/watchForum/%s.page";
    public static final String UNWATCH_FORUM_URL = "http://members.iracing.com/jforum/forums/unwatchForum/%s.page";

    public static Result getSource(Context context, String url)
    {
        try
        {
            NetworkMethod method = ForumMethod.Get(url);
            HttpResult result = method.execute(context);
            return Result.ok(result);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error while getting source.", ex);
        }
    }

	public static Result login(Context context, User user)
	{
        NetworkMethod.resetCookieStore();

		Params param = new Params();
		param.put("username", HttpUtil.htmlEncode(user.username));
		param.put("password", HttpUtil.htmlEncode(user.password));

		try 
		{
			NetworkMethod method = ForumMethod.Get(LOGIN_URL);
            Log.i("Login", "Start executing.");
            HttpResult result = method.execute(context, param);
            Log.i("Login", "Finished executing.");

			return Result.ok(result);
		}
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
			return Result.error("Error during login.", ex);
		}
	}
	
	public static Result getForumList(Context context)
	{
		try
		{
			NetworkMethod method = ForumMethod.Get(FORUMLIST_URL);
            HttpResult result  = method.execute(context);
			return Result.ok(result);
		}
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
			return Result.error("Error connecting to forum list page.", ex);
		}
	}
	
	public static Result getThreadList(Context context, String url)
	{
		try
		{
			url = Urls.getForumUrl(url);
			NetworkMethod method = ForumMethod.Get(url);
            HttpResult result  = method.execute(context);
			return Result.ok(result);
		}
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
			return Result.error("Error connecting to thread list page.", ex);
		}
	}

    public static Result getPostList(Context context, String url)
    {
        try
        {
            url = Urls.getForumUrl(url);
            NetworkMethod method = ForumMethod.Get(url);
            HttpResult result  = method.execute(context);
            return Result.ok(result);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error connecting to thread page.", ex);
        }
    }

    public static Result getRecentPopular(Context context, int type)
    {
        try
        {
            String url = type == RecentPopularActivity.POPULAR ? POPULAR_URL : RECENT_URL;
            NetworkMethod method = ForumMethod.Get(url);
            HttpResult result = method.execute(context);
            return Result.ok(result);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error connecting to recent/popular page.", ex);
        }
    }

    public static Result getPmInbox(Context context)
    {
        return getPmList(context, false);
    }

    public static Result getPmSentbox(Context context)
    {
        return getPmList(context, true);
    }

    private static Result getPmList(Context context, boolean sent)
    {
        try
        {
            String url = sent ? SENTPMLIST_URL : PMLIST_URL;
            NetworkMethod method = ForumMethod.Get(url);
            HttpResult result = method.execute(context);
            return Result.ok(result);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error connecting to PM list page.", ex);
        }
    }

    public static Result getPm(Context context, String url)
    {
        try
        {
            NetworkMethod method = ForumMethod.Get(url);
            HttpResult result = method.execute(context);
            return Result.ok(result);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error connecting to PM page.", ex);
        }
    }

    public static Result getQuoteContents(Context context, String url)
    {
        try
        {
            NetworkMethod method = ForumMethod.Get(url);
            HttpResult response = method.execute(context);
            if (response != null) return Result.ok(response);
            return Result.failed();
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error connecting to quote page.", ex);
        }
    }

    public static Result getEditContents(Context context, String url)
    {
        try
        {
            NetworkMethod method = ForumMethod.Get(url);
            HttpResult response = method.execute(context);
            if (response != null) return Result.ok(response);
            return Result.failed();
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error connecting to edit page.", ex);
        }
    }

    public static Result getDrivers(Context context, String username)
    {
        try
        {
            Params param = new Params();
            param.put("searchTerms", username);

            NetworkMethod method = ForumMethod.Get(DRIVERSEARCH_URL);
            HttpResult response = method.execute(context, param);
            if (response != null) return Result.ok(response);
            return Result.failed();
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error connecting to member search page.", ex);
        }
    }

    public static Result sendPm(Context context,
                                String username,
                                int custid,
                                String title,
                                String text)
    {
        try
        {
            Params param = new Params();
            param.put("action", "sendSave");
            param.put("module", "pm");
            param.put("preview", 0);
            param.put("toUsername", username);
            param.put("toUserId", custid);
            param.put("subject", title);
            param.put("message", text);

            NetworkMethod method = ForumMethod.Get(PM_URL);
            HttpResult response = method.execute(context, param);
            if (response != null) return Result.ok(response);
            return Result.failed();
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error sending message.", ex);
        }
    }

    public static Result postThread(Context context,
                                     int forumId,
                                     String title,
                                     String text,
                                     boolean attachSig)
    {
        try
        {
            Params param = new Params();
            param.put("action", "insertSave");
            param.put("module", "posts");
            param.put("forum_id", forumId);
            param.put("start", 0);
            param.put("preview", 0);
            param.put("disable_html", 1);
            param.put("subject", title);
            param.put("message", text);
            if (attachSig) param.put("attach_sig", "on");

            NetworkMethod method = ForumMethod.Get(NEWTHREAD_URL);
            HttpResult response = method.execute(context, param);
            if (response != null) return Result.ok(response);
            return Result.failed();
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error creating thread.", ex);
        }
    }

    public static Result postMessage(Context context,
                                     int forumId,
                                     int threadId,
                                     String text,
                                     boolean attachSig)
    {
        try
        {
            Params param = new Params();
            param.put("action", "insertSave");
            param.put("module", "posts");
            param.put("forum_id", forumId);
            param.put("start", 0);
            param.put("topic_id", threadId);
            param.put("disable_html", 1);
            param.put("message", text);
            if (attachSig) param.put("attach_sig", "on");

            NetworkMethod method = ForumMethod.Get(POST_URL);
            HttpResult response = method.execute(context, param);
            if (response != null) return Result.ok(response);
            return Result.failed();
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error posting message.", ex);
        }
    }

    public static Result editMessage(Context context,
                                     int forumId,
                                     int threadId,
                                     int postId,
                                     String text)
    {
        try
        {
            Params param = new Params();
            param.put("action", "editSave");
            param.put("module", "posts");
            param.put("forum_id", forumId);
            param.put("start", 0);
            param.put("topic_id", threadId);
            param.put("post_id", postId);
            param.put("disable_html", 1);
            param.put("message", text);

            NetworkMethod method = ForumMethod.Get(EDIT_URL);
            HttpResult response = method.execute(context, param);
            if (response != null) return Result.ok(response);
            return Result.failed();
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error posting message.", ex);
        }
    }

    public static Result toggleWatchThread(Context context, Thread thread)
    {
        try
        {
            String url;
            if (thread.IsWatching)
                url = String.format(UNWATCH_URL, thread.Id);
            else
                url = String.format(WATCH_URL, thread.Id);

            NetworkMethod method = ForumMethod.Get(url);
            method.execute(context);
            thread.IsWatching = !thread.IsWatching;
            return Result.ok(null);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error toggling watch status.", ex);
        }
    }

    public static Result toggleWatchForum(Context context, Forum forum)
    {
        try
        {
            String url;
            if (forum.IsWatching)
                url = String.format(UNWATCH_FORUM_URL, forum.Id);
            else
                url = String.format(WATCH_FORUM_URL, forum.Id);

            NetworkMethod method = ForumMethod.Get(url);
            method.execute(context);
            forum.IsWatching = !forum.IsWatching;
            return Result.ok(null);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error toggling watch status.", ex);
        }
    }

    ///////////////////////////////////////////////////////////////////////
    /// Communication with webservice
    ///////////////////////////////////////////////////////////////////////

    private static User getUser()
    {
        User user = User.currentUser;
        if (user == null)
        {
            user = new User();
            user.username = "(none)";
            user.realname = "";
        }
        return user;
    }

    public static Result addFavorite(Context context, User user, FavoriteThread thread)
    {
        try
        {
            Params param = Params.createServiceParams();
            param.put("username", user.username);
            param.put("threadId", thread.ThreadId);
            param.put("threadName", thread.ThreadName);
            param.put("threadUrl", thread.ThreadUrl);
            param.put("forumId", thread.ForumId);

            NetworkMethod method = ServiceMethod.Post("/AddFavorite");
            HttpResult result = method.execute(context, param);
            return Result.fromJson(result.Contents);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error uploading favorites.", ex);
        }
    }

    public static Result deleteFavorite(Context context, User user, FavoriteThread thread)
    {
        try
        {
            Params param = Params.createServiceParams();
            param.put("username", user.username);
            param.put("threadId", thread.ThreadId);

            NetworkMethod method = ServiceMethod.Post("/DeleteFavorite");
            HttpResult result = method.execute(context, param);
            return Result.fromJson(result.Contents);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return Result.error("Error deleting favorites.", ex);
        }
    }

    public static FavThreadsResult getFavorites(Context context, User user)
    {
        try
        {
            Params param = Params.createServiceParams();
            param.put("username", user.username);

            NetworkMethod method = ServiceMethod.Post("/GetFavorites");
            HttpResult result = method.execute(context, param);
            return FavThreadsResult.fromJson(result.Contents);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return FavThreadsResult.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return FavThreadsResult.error("Error importing favorites.", ex);
        }
    }

    public static StringArrayResult getDonations(Context context)
    {
        try
        {
            Params param = Params.createServiceParams();
            NetworkMethod method = ServiceMethod.Post("/GetDonations");
            HttpResult result = method.execute(context, param);
            return StringArrayResult.fromJson(result.Contents);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return StringArrayResult.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return StringArrayResult.error("Error retrieving donated users.", ex);
        }
    }

    public static StringArrayResult getForumNames(Context context)
    {
        try
        {
            Params param = Params.createServiceParams();
            NetworkMethod method = ServiceMethod.Post("/GetForumNames");
            HttpResult result = method.execute(context, param);
            return StringArrayResult.fromJson(result.Contents);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return StringArrayResult.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return StringArrayResult.error("Error retrieving forum names.", ex);
        }
    }
    public static StringResult getMessage(Context context, User user)
    {
        try
        {
            Params param = Params.createServiceParams();
            param.put("username", user.username);

            NetworkMethod method = ServiceMethod.Post("/GetMessage");
            HttpResult result = method.execute(context, param);
            return StringResult.fromJson(result.Contents);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return StringResult.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            return StringResult.error("Error retrieving messages.", ex);
        }
    }

    public static Result logError(Context context, Error error)
    {
        try
        {
            User user = getUser();
            Params param = Params.createServiceParams();
            param.put("username", user.username);
            param.put("realname", user.realname);
            param.put("details", error.message);
            param.put("exception", error.detailMessage);

            NetworkMethod method = ServiceMethod.Post("/LogError");
            method.execute(context, param);
            return Result.ok(null);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            Log.e("LogError", "Error while sending error to service.");
            return Result.failed();
        }
    }

    public static Result logLogin(Context context, User user)
    {
        try
        {
            Params param = Params.createServiceParams();
            param.put("username", user.username);
            param.put("realname", user.realname);

            NetworkMethod method = ServiceMethod.Post("/LogLogin");
            method.execute(context, param);
            return Result.ok(null);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            Log.e("LogLogin", "Error while sending login to service.");
            return Result.failed();
        }
    }

    public static Result logPageRequest(Context context, String url)
    {
        try
        {
            User user = getUser();
            Params param = Params.createServiceParams();
            param.put("username", user.username);
            param.put("realname", user.realname);
            param.put("url", url);

            NetworkMethod method = ServiceMethod.Post("/LogPageRequest");
            method.execute(context, param);
            return Result.ok(null);
        }
        catch (TimeoutException tex)
        {
            Log.e("Timeout", "Timed out.");
            return Result.error("Connection timed out.", tex);
        }
        catch (Exception ex)
        {
            Log.e("LogPageRequest", "Error while sending page request to service.");
            return Result.failed();
        }
    }
}
